﻿/************************************************************************
 * SMLib - State Model Library
 * Copyright (C) 2012-2013 Ali Khalili (khalili_DOT_ir_@SIGN_gmail_DOT_com)
 * **********************************************************************
 * This program is free software; you can redistribute it and/or modify it
 * under the terms of the GNU Lesser General Public License (LGPL) published 
 * by the Free Software Foundation; either version 2.1 of the License, or (at your
 * option) any later version. This program is distributed without any
 * warranty. See the GNU Lesser General Public License for more details. 
 * ***********************************************************************
 * File         :     SMLib\Automata\I\IA_Generator.cs
 * Description  :     Generation of random IAs
 * Change log   :     -Jun 2014:  Initial version
 *************************************************************************/
using System;

namespace SMLib.Automata.IA
{
	/// <summary>
	/// Description of IA_Generator.
	/// <remarks>This is to generate random interface automata</remarks>
	/// </summary>
	public class IA_Generator
	{
		int numberOfStates;
		int numberOfOutputStates;
		int SigISize;
		int SigOSize;
		int outputDegree;
		//TODO add separate and not separate form
		//bool separated;
		
		private IA_Generator(int numberOfStates,int numberOfOutputStates,int SigISize,int SigOSize,int outputDegree,bool separated)
		{
			this.numberOfStates=numberOfStates;
			this.numberOfOutputStates=numberOfOutputStates;
			this.SigOSize=SigOSize;
			this.SigISize=SigISize;
			//this.separated=separated;
			this.outputDegree=outputDegree;
		}
		
		/// <summary>
		/// Generate a Random IA
		/// </summary>
		/// <param name="numberOfStates">number of states</param>
		/// <param name="numberOfOutputStates">number of states with only output</param>
		/// <param name="SigISize">the size of input alphabet</param>
		/// <param name="SigOSize">the size of output alphabet</param>
		/// <param name="outputDegree">number of output transitions in states with output</param>
		/// <param name="separated">should the output and input states be separated?</param>
		/// <returns></returns>
		public static InterfaceAutomaton GenerateRandomIA(int numberOfStates,int numberOfOutputStates,int SigISize,int SigOSize,int outputDegree,bool separated)
		{
			IA_Generator gen=new IA_Generator(numberOfStates,numberOfOutputStates,SigISize,SigOSize,outputDegree,separated);
			return gen.Generate();
		}
		
		InterfaceAutomaton Generate()
		{
			SMLib.Automata.Base.Alphabet inputs=new SMLib.Automata.Base.Alphabet();
			for(int i=0;i<SigISize;i++)
			{
				SMLib.Automata.Base.StringSymbol str=new SMLib.Automata.Base.StringSymbol("i_"+i);
				inputs.AddSymbol(str);
			}
			SMLib.Automata.Base.Alphabet outputs=new SMLib.Automata.Base.Alphabet();			
			for(int i=0;i<SigOSize;i++)
			{
				SMLib.Automata.Base.StringSymbol str=new SMLib.Automata.Base.StringSymbol("o_"+i);
				outputs.AddSymbol(str);
			}
				
			InterfaceAutomaton ia=new InterfaceAutomaton();
			Random rng=new Random();
			
			//* generate states
			for(int i=0;i<numberOfStates;i++)
			{
				ia.AddState(new InterfaceAutomaton.IA_State(i));
			}
			
			for(int i=0;i<numberOfStates;i++)
			{
				bool ifO=(rng.NextDouble()<=(((double)numberOfOutputStates)/numberOfStates)?true:false);
				if(ifO)
				{
					//* I assume that this degree is one !
					outputDegree=1;
					//* output states
					for(int j=0;j<outputDegree;j++)
					{
						int oInd;						
						oInd=rng.Next(SigOSize);			
						int indexOfDest;
						do{
							indexOfDest= rng.Next(numberOfStates);
						} while(indexOfDest!=i);
						InterfaceAutomaton.IA_Step tr=new InterfaceAutomaton.IA_Step(outputs[oInd],false);						
						tr.SetDestination(ia[indexOfDest]);
						ia[i].AddTransition(tr);					
					}
				}
				else
				{
					//* input states
					for(int j=0;j<SigISize;j++)
					{
						int indexOfDest;
						do{
							indexOfDest= rng.Next(numberOfStates);
						} while(indexOfDest!=i);
						InterfaceAutomaton.IA_Step tr=new InterfaceAutomaton.IA_Step(inputs[j],true);
						tr.SetDestination(ia[indexOfDest]);
						ia[i].AddTransition(tr);
					}
				}
			}
			return ia;		
		}
	}
}